#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "codegen_visitor.h"

static int stack_size = -1;
FILE* fb;

static void _print_boolean(TreeNode *node);
static void _print_load(TreeNode *node, Visitor *visitor);
static int _get_type_size(t_kind type);
static int _process_expression(TreeNode *rnode, Visitor *visitor);

Visitor *codegen_newVisitor()
{
	Visitor *visitor = (Visitor *) malloc(sizeof(Visitor));
	visitor->visitorFunction = visitGenFunctions;
    return visitor;
}

void
codegen_visit_program(Visitor *visitor, TreeNode *node)
{
    TreeNode *child;

    printf("; Portal code\n");
    for (child = node->children;
         child != NULL && child->kind != INSTRUCAO_LISTA;
         child = child->sibling) {
    	tree_node_accept_children(child, visitor);
    }

    if (child != NULL) {
        printf("; Definition of main function\n");
        printf("define i32 @main () {\nentry:\n");
        tree_node_accept_children(child, visitor);
        printf(TAB"ret i32 0\n}\n\n");
    }
}

void
codegen_visit_programdecl(Visitor *visitor, TreeNode *node)
{
    printf("; program ");
    tree_node_accept_children(node->children, visitor);
    printf("\n\n");
    printf("; Declare the string constants as a global constants...\n");
    printf("@bool_str = global [2 x i8*] [ "
           "i8* getelementptr ([6 x i8]* @.false_str, i32 0, i32 0), "
           "i8* getelementptr ([5 x i8]* @.true_str, i32 0, i32 0) ]\n");
    printf("@.false_str = internal constant [6 x i8] c\"false\\00\"\n");
    printf("@.true_str = internal constant [5 x i8] c\"true\\00\"\n");
    printf("@.int_fmt = internal constant [3 x i8] c\"%%d\\00\"\n");
    printf("@.bool_fmt = internal constant [3 x i8] c\"%%s\\00\"\n");

    printf("\n; External declaration of functions\n");
    printf("declare i32 @puts(i8 *)\n");
    printf("declare i32 @putchar(i32)\n");
    printf("declare i32 @printf(i8*, ...)\n\n");
}

void
codegen_visit_vardecl_list (Visitor *visitor, TreeNode *node)
{
	tree_node_accept_children(node->children, visitor);
    if (node->parent->kind == PROGRAMA)
        printf("\n");
}

void
codegen_visit_identifier_list (Visitor *visitor, TreeNode *node)
{
    TreeNode *child;

    /* FIXME */ fprintf(stderr, "Never reaches here?\n");
    for (child = node->children; child != NULL; child = child->sibling) {
    	tree_node_accept_children(child, visitor);
        if (child->sibling != NULL)
            printf(", ");
    }
}

void
codegen_visit_procfunc_list (Visitor *visitor, TreeNode *node)
{
	tree_node_accept_children( node->children, visitor );
    //ast_node_accept_children(node->children, visitor);
}

void
codegen_visit_procfunc (Visitor *visitor, TreeNode *node)
{
    TreeNode *child;

    printf("define ");
    PRINT_TYPE(node->type);
    printf(" ");

    child = node->children; // Identifier
    tree_node_accept_children( child, visitor );
    //ast_node_accept(child, visitor);

    printf(" ( ");

    child = child->sibling;
    /*if (child->kind == PARAMETER_LIST) {
        ast_node_accept(child, visitor);
        child = child->sibling;
    }*/

    printf(" ) {\n");
    printf("entry:\n");

    if (child->kind == VARIAVEL) {
    	tree_node_accept_children(child, visitor);
        child = child->sibling;
    }

    tree_node_accept_children(child, visitor);

    //printf(TAB"ret ");
    //PRINT_TYPE(node->type);
    /*if (node->kind == FUNCAO) {
        printf(" ");
        PRINT_VALUE(node->children, node->children->name->stack_index);
    }*/

    printf("\n}\n\n");
}
/*
void
codegen_visit_param_list (Visitor *visitor, TreeNode *node)
{
    TreeNode *child;

    for (child = node->children; child != NULL; child = child->sibling) {
        ast_node_accept(child, visitor);
        if (child->sibling != NULL)
            printf(", ");
    }
}

void
codegen_visit_statement_list (Visitor *visitor, TreeNode *node)
{
    TreeNode *child;

    stack_size = -1;

    for (child = node->children; child != NULL; child = child->sibling) {
        ast_node_accept(child, visitor);
        printf("\n");
    }
}*/
//
//void
//codegen_visit_binary_expr (Visitor *visitor, TreeNode *node)
//{
//    int lindex = -1;
//    int rindex = -1;
//    TreeNode *lnode = node->children;
//    TreeNode *op = lnode->sibling;
//    TreeNode *rnode = op->sibling;
//
//    int __process_binexpr_node(TreeNode *node) {
//        if (node->kind == IDENTIFICADOR) {
//            if (node->name->is_global) {
//                if (name_is_procfunc(node->name))
//                    return node->name->stack_index;
//
//                _print_load(node, visitor);
//                return stack_size;
//            }
//
//        } else if (!IS_LITERAL(node->kind)) {
//            ast_node_accept(node, visitor);
//            return stack_size;
//
//        }
//        return -1;
//    }
//
//    void __print_operand(TreeNode *node, int index) {
//        if (index > -1)
//            printf("%%%d", index);
//        else if (node->name != NULL && name_is_procfunc(node->name))
//            printf("0");
//        else
//            ast_node_accept(node, visitor);
//    }
//
//    /* Construcao mais simples */
//    if (IS_LITERAL(lnode->kind) && IS_LITERAL(rnode->kind)) {
//        ast_node_accept(op, visitor);
//        printf(" ");
//        PRINT_TYPE(lnode->type);
//        printf(" ");
//        ast_node_accept(lnode, visitor);
//        printf(", ");
//        ast_node_accept(rnode, visitor);
//        printf("\n");
//
//    /* Construcoes complexas */
//    } else {
//        lindex = __process_binexpr_node(lnode);
//        rindex = __process_binexpr_node(rnode);
//
//        ast_node_accept(op, visitor);
//        printf(" ");
//        PRINT_TYPE(lnode->type);
//        printf(" ");
//
//        __print_operand(lnode, lindex);
//        printf(", ");
//        __print_operand(rnode, rindex);
//
//        printf("\n");
//    }
//
//    stack_size++;
//}
//
//void
//codegen_visit_callparam_list (Visitor *visitor, TreeNode *node)
//{
//    ast_node_accept(node->children, visitor);
//}
//
//void
//codegen_visit_callparam (Visitor *visitor, TreeNode *node)
//{
//    ast_node_accept(node->children, visitor);
//}

/*
void
codegen_visit_identifier (Visitor *visitor, TreeNode *node)
{
    name *sym = node->name;

    if (sym->is_global || node->parent->kind == CALL)
        printf("@%s", sym->name);

    else if (!sym->is_global && node->parent->kind == PARAMETER &&
             sym->stack_index == -1)
        printf("%%%s", sym->name);

    else if (sym->stack_index > -1)
        printf("%%%d", sym->stack_index);

    else
        printf("0");
}
*/
void
codegen_visit_literal (Visitor *visitor, TreeNode *node)
{
    printf("%d", node->value.inteiro);
}

void
codegen_visit_vardecl (Visitor *visitor, TreeNode *node)
{
    TreeNode *child;

    child = node->children;

    for (child = child->children; child != NULL; child = child->sibling) {
    	tree_node_accept_children(child, visitor);
    	printf(" = global i%d 0\n", _get_type_size(child->type));
    }
}
/*
void
codegen_visit_parameter (Visitor *visitor, TreeNode *node)
{
    PRINT_TYPE(node->type);
    printf(" ");
    ast_node_accept(node->children, visitor);
}
*/
/*
void
codegen_visit_printint_stmt (Visitor *visitor, TreeNode *node)
{
    int index = -1;
    TreeNode *child = node->children;

    index = _process_expression(child, visitor);

    printf(TAB"call i32 (i8* noalias , ...)* bitcast (i32 (i8*, ...)* \n");
    printf(TAB TAB"@printf to i32 (i8* noalias, ...)*)\n");
    printf(TAB TAB"( i8* getelementptr ");
    printf("([3 x i8]* @.int_fmt, i32 0, i32 0) noalias ,\n");
    printf(TAB TAB"i32 ");
    PRINT_VALUE(child, index);

    printf(" )\n");
    stack_size++;
}*/

/*
void
codegen_visit_printchar_stmt (Visitor *visitor, TreeNode *node)
{
    int index = -1;
    TreeNode *child = node->children;

    index = _process_expression(child, visitor);

    printf(TAB"call i32 @putchar ( i32 ");
    PRINT_VALUE(child, index);
    printf(" )\n");
    stack_size++;
}*/

//void
//codegen_visit_printbool_stmt (Visitor *visitor, TreeNode *node)
//{
//    int index = -1;
//    TreeNode *child = node->children;
//
//    index = _process_expression(child, visitor);
//
//    if (index == -1) {
//        printf(TAB"load i8** getelementptr ([2 x i8*]* @bool_str, i32 0, i32 %d )"
//               ", align 4\n", ast_node_get_value_as_int(child));
//    } else {
//        printf(TAB"getelementptr [2 x i8*]* @bool_str, i32 0, i32 %%%d\n",
//               index);
//        stack_size++;
//        printf(TAB"load i8** %%%d, align 4\n", stack_size);
//    }
//    stack_size++;
//
//    printf(TAB"call i32 (i8* noalias , ...)* bitcast (i32 (i8*, ...)* \n");
//    printf(TAB TAB"@printf to i32 (i8* noalias , ...)*)\n");
//    printf(TAB TAB"( i8* getelementptr ");
//    printf("([3 x i8]* @.bool_fmt, i32 0, i32 0) noalias , \n");
//    printf(TAB TAB"i8* %%%d )\n", stack_size);
//    stack_size++;
//}

//void
//codegen_visit_printline_stmt (Visitor *visitor, TreeNode *node)
//{
//    printf(TAB"call i32 @putchar( i32 10 )\n");
//    stack_size++;
//}
//
//void
//codegen_visit_assignment_stmt (Visitor *visitor, TreeNode *node)
//{
//    int rindex = -1;
//    TreeNode *lnode = node->children;
//    TreeNode *rnode = lnode->sibling;
//
//    /* FIXME * */printf("; [Assignment][%s] %s(%d/%d) = %d\n", rnode->name,
//                       lnode->name, lnode->name->stack_index,
//                       stack_size, ast_node_get_value_as_int(rnode));
//    /**/
//
//    /* rnode */
//    rindex = _process_expression(rnode, visitor);
//
//    /* lnode */
//    if (!name_is_procfunc(lnode->name)) {
//        printf(TAB"store ");
//        PRINT_TYPE(lnode->type);
//        printf(" ");
//        PRINT_VALUE(rnode, rindex);
//        printf(", ");
//        PRINT_TYPE(lnode->type);
//        printf("* ");
//        ast_node_accept(lnode, visitor);
//        printf(", align 4\n");
//
//    } else if (rindex == -1) {
//        /*lnode->name->stack_index = -1;
//        lnode->name->value.integer = rnode->value.integer;
//        lnode->value.integer = rnode->value.integer;*/
//        printf(TAB"add ");
//        PRINT_TYPE(lnode->type);
//        printf(" ");
//        PRINT_VALUE(rnode, rindex);
//        printf(", 0\n");
//        stack_size++;
//        lnode->name->stack_index = stack_size;
//
//    } else
//        lnode->name->stack_index = rindex;
//
//
//    /* FIXME */ printf("; [Assignment][%s] %s(%d/%d) = %d\n", rnode->name,
//                       lnode->name, lnode->name->stack_index,
//                       stack_size, ast_node_get_value_as_int(rnode));
//    /**/
//}

/*
void
codegen_visit_if_stmt (Visitor *visitor, TreeNode *node)
{
    int index = -1;
    TreeNode *expr = node->children;
    TreeNode *cmd1 = expr->sibling;
    TreeNode *cmd2 = cmd1->sibling;

    printf("; if evaluation, line %d\n", node->linenum);

    index = _process_expression(expr, visitor);

    printf(TAB"br i1 ");
    PRINT_VALUE(expr, index);
    printf(", label %%cond_true_%x, label ", node);

    if (cmd2 == NULL)
        printf("%%cond_next_%x\n", node);
    else
        printf("%%cond_false_%x\n", node);

    printf("\ncond_true_%x:\n", node);
    ast_node_accept(cmd1, visitor);
    printf(TAB"br label %%cond_next_%x\n", node);

    if (cmd2 != NULL) {
        printf("\ncond_false_%x:\n", node);
        ast_node_accept(cmd2, visitor);
        printf(TAB"br label %%cond_next_%x\n", node);
    }

    printf("\ncond_next_%x:\n", node);
}
*/
//void
//codegen_visit_while_stmt (Visitor *visitor, TreeNode *node)
//{
//    TreeNode *child;
//    const char *var;
//
//    printf("while (");
//    child = node->children; // Expression
//    ast_node_accept(child, visitor);
//    printf(") {\n");
//
//    child = child->sibling; // Statements
//    ast_node_accept(child, visitor);
//
//    printf("}\n");
//}

//void
//codegen_visit_for_stmt (Visitor *visitor, TreeNode *node)
//{
//    int index = -1;
//    TreeNode *asgn = node->children;
//    TreeNode *expr = asgn->sibling;
//    TreeNode *stmt = expr->sibling;
//
//    printf("; for evaluation, line %d\n", node->linenum);
//
//    //%tmp512 = ????
//    ast_node_accept(asgn, visitor);
//
///*
//    %tmp714 = icmp sgt i32 %tmp512, 0       ; <i1> [#uses=1]
//    br i1 %tmp714, label %bb.preheader, label %bb9
//    */
//    index = _process_expression(expr, visitor);
//
//    printf(TAB"br i1 ");
//    PRINT_VALUE(expr, index);
//    printf(", label %%bb_%x.preheader, label %%bb_%x\n", node, node);
//
///*
//bb.preheader:       ; preds = %entry
//    %b.promoted = load i32* @b, align 4     ; <i32> [#uses=1]
//    br label %bb
//    */
//    printf("\nbb_%x.preheader:\n", node);
//
//
///*
//bb:     ; preds = %bb, %bb.preheader
//    %ITERADOR.010.0 = phi i32 [ 0, %bb.preheader ], [ %tmp3, %bb ]      ; <i32> [#uses=2]
//    %tmp3 = add i32 %ITERADOR.010.0, 1      ; <i32> [#uses=2]
//    %tmp7 = icmp slt i32 %tmp3, %tmp512     ; <i1> [#uses=1]
//    br i1 %tmp7, label %bb, label %bb9.loopexit
//    */
//
///*
//bb9.loopexit:       ; preds = %bb
//    %b.tmp.0 = add i32 %ITERADOR.010.0, %b.promoted     ; <i32> [#uses=1]
//    %tmp1 = add i32 %b.tmp.0, 1     ; <i32> [#uses=1]
//    store i32 %tmp1, i32* @b, align 4
//    br label %bb9
//*/
//
///*
//bb9:        ; preds = %bb9.loopexit, %entry
//*/
//
//
//    printf("\ncond_true_%x:\n", node);
//    ast_node_accept(stmt, visitor);
//    printf(TAB"br label %%cond_next_%x\n", node);
//
//    printf("\ncond_next_%x:\n", node);
//}

void
codegen_visit_notfactor (Visitor *visitor, TreeNode *node)
{
	tree_node_accept_children(node->children, visitor);
}

void
codegen_visit_call (Visitor *visitor, TreeNode *node)
{
/*
    int index = -1;
    TreeNode *temp, *child = node->children;

    index = _process_expression(child, visitor);

    temp= child->sibling;
    if (temp != NULL) {
        for (temp = temp->children; temp != NULL; temp = temp->sibling) {
            PRINT_TYPE(child->type);
            printf(" ");
            ast_node_accept(child, visitor);
            if (child->sibling != NULL)
                printf(", ");
        }
    }

    printf(TAB"call ");
    PRINT_TYPE(child->name->type);
    printf(" ");
    ast_node_accept(child, visitor);
    printf("( ");

    printf(" )\n");
    stack_size++;
*/
    TreeNode *child = node->children;

    printf(TAB"call ");
    PRINT_TYPE(child->kind);
    printf(" ");
    tree_node_accept_children(child, visitor);
    printf("( ");

    child = child->sibling;
    if (child != NULL) {
        for (child = child->children; child != NULL; child = child->sibling) {
            PRINT_TYPE(child->type);
            printf(" ");
            tree_node_accept_children(child, visitor);
            if (child->sibling != NULL)
                printf(", ");
        }
    }

    printf(" )\n");
    stack_size++;
}

void
codegen_visit_simplenode (Visitor *visitor, TreeNode *node)
{
	tree_node_accept_children(node->children, visitor);
}

void
codegen_visit_binary_op (Visitor *visitor, TreeNode *node)
{
    /* FIXME *printf("; %%%d = \n", stack_size + 1);*/
    switch (node->kind) {
        case T_OP_OU:
            printf(TAB"or");
            break;
        case T_OP_E:
            printf(TAB"and");
            break;
        case T_OP_IGUAL:
            printf(TAB"icmp eq");
            break;
        case T_OP_DIFERENTE:
            printf(TAB"icmp ne");
            break;
        case T_OP_MENOR:
            printf(TAB"icmp slt");
            break;
        case T_OP_MAIOR:
            printf(TAB"icmp sgt");
            break;
        case T_OP_MENORIGUAL:
            printf(TAB"icmp sle");
            break;
        case T_OP_MAIORIGUAL:
            printf(TAB"icmp sge");
            break;
        case T_OP_ADICAO:
            printf(TAB"add");
            break;
        case T_OP_SUBTRACAO:
            printf(TAB"sub");
            break;
        case T_OP_MULTIPLICACAO:
            printf(TAB"mul");
            break;
        /*case T_SLASH:
            printf(" %s ", node->name);*/
    }
}

void
codegen_visit_not_op (Visitor *visitor, TreeNode *node)
{
    printf(" !", node->name);
}

static void
_print_boolean(TreeNode *node)
{
    printf("%bool_str = getelementptr [");
    if (node->type == BOOLEANO )
        printf("5 x i8]* @.true");
    else
        printf("6 x i8]* @.false");
    printf("_str, i32 0, i32 0\n");
}

static void
_print_load(TreeNode *node, Visitor *visitor)
{
    printf(TAB"load ");
    PRINT_TYPE(node->type);
    printf("* ");
    tree_node_accept_children(node, visitor);
    printf(", align 4\n");
    stack_size++;
}

static int
_get_type_size(t_kind type)
{
    switch (type) {
        case INTEIRO:
            return 32;
        case CARACTER:
            return 8;
        case BOOLEANO:
            return 1;
        default:
            return 0;
    }
}

//static int
//_process_expression(TreeNode *rnode, Visitor *visitor)
//{
//    if (!IS_LITERAL(rnode->kind)) {
//        if (rnode->kind != IDENTIFICADOR) {
//            ast_node_accept(rnode, visitor);
//            return stack_size;
//
//        }
//        /*else if (rnode->name->is_global)
//        {
//            _print_load(rnode, visitor);
//            return stack_size;
//
//        }*/
//        else
//            return rnode->name->stack_index;
//
//    }
//
//    return -1;
//}
